﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Threading;

namespace ProjectN.Core.Instance
{
    /// <summary>
    /// 提供基础文件存储管理功能的泛型基类
    /// </summary>
    /// <typeparam name="T">继承自 FileHandler<T> 的具体处理器类型</typeparam>
    public abstract class ResourceCollection<T> : IDisposable where T : ResourceItem<T>
    {
        /// <summary> 文件存储根目录路径 </summary>
        protected readonly string _storagePath;

        /// <summary> 标识值与文件处理器的字典缓存 </summary>
        protected readonly Dictionary<string, T> _handlers = new Dictionary<string, T>();

        /// <summary> 线程安全读写锁 </summary>
        protected readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        private bool _disposed;

        /// <summary>
        /// 初始化文件管理器实例
        /// </summary>
        /// <param name="storagePath">文件存储根目录路径</param>
        protected ResourceCollection(string storagePath)
        {
            _storagePath = Directory.CreateDirectory(storagePath).FullName;
        }

        /// <summary>
        /// 通过字节数组存储文件
        /// </summary>
        /// <param name="fileData">文件数据</param>
        /// <param name="extension">文件扩展名（包含前导点，如 ".jpg"）</param>
        /// <returns>指向该文件的 <seealso cref="ResourceItem{T}"/> 实例</returns>
        public T StoreFile(byte[] fileData, string extension)
        {
            CheckDisposed();
            var tempPath = Path.GetTempFileName();
            try
            {
                File.WriteAllBytes(tempPath, fileData);
                return ProcessFile(tempPath, extension);
            }
            finally
            {
                File.Delete(tempPath);
            }
        }

        /// <summary>
        /// 通过文件路径存储文件
        /// </summary>
        /// <param name="filePath">源文件路径</param>
        /// <returns>指向该文件的 <seealso cref="ResourceItem{T}"/> 实例</returns>
        public T StoreFile(string filePath)
        {
            CheckDisposed();
            return ProcessFile(filePath, Path.GetExtension(filePath));
        }

        // 处理文件存储的核心方法
        private T ProcessFile(string sourcePath, string extension)
        {
            var hash = CalculateIdentity(sourcePath);
            var targetPath = GetFilePath(hash, extension);

            try
            {
                // 获取可升级读锁以检查哈希存在性
                _lock.EnterUpgradeableReadLock();

                if (!_handlers.ContainsKey(hash))
                {
                    try
                    {
                        // 升级为写锁以添加新条目
                        _lock.EnterWriteLock();

                        // 确保目标文件存在
                        if (!File.Exists(targetPath))
                        {
                            File.Copy(sourcePath, targetPath);
                        }

                        // 双重检查防止竞态条件
                        if (!_handlers.ContainsKey(hash))
                        {
                            _handlers[hash] = CreateHandler(hash, extension);
                        }
                    }
                    finally
                    {
                        _lock.ExitWriteLock();
                    }
                }
                return _handlers[hash];
            }
            finally
            {
                _lock.ExitUpgradeableReadLock();
            }
        }

        /// <summary>
        /// 创建具体的文件处理器实例
        /// </summary>
        /// <param name="hash">文件标识值</param>
        /// <param name="extension">文件扩展名</param>
        /// <returns>新创建的文件处理器实例</returns>
        protected abstract T CreateHandler(string hash, string extension);

        /// <summary>
        /// 获取标识对应的文件路径
        /// </summary>
        /// <param name="identity">文件标识值</param>
        /// <param name="extension">文件扩展名</param>
        /// <returns>完整的文件存储路径</returns>
        internal string GetFilePath(string identity, string extension)
            => Path.Combine(_storagePath, $"{identity}{extension.ToLowerInvariant()}");

        /// <summary>
        /// 计算文件标识值
        /// </summary>
        /// <param name="filePath">路径</param>
        /// <returns>标识值</returns>
        internal virtual string CalculateIdentity(string filePath)
        {
            using var sha256 = SHA256.Create();
            using var stream = File.OpenRead(filePath);
            return BitConverter.ToString(sha256.ComputeHash(stream)).Replace("-", "").ToLowerInvariant();
        }

        /// <summary>
        /// 通过标识值获取文件处理器
        /// </summary>
        /// <param name="hash">要查找的文件的标识值</param>
        /// <returns>指向该文件的 <seealso cref="ResourceItem{T}"/> 实例，不存在时返回 null</returns>
        public T? GetHandler(string hash)
        {
            CheckDisposed();
            try
            {
                _lock.EnterReadLock();
                return _handlers.TryGetValue(hash, out var handler) ? handler : null;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        // 检查对象是否已被释放
        private void CheckDisposed()
        {
            ObjectDisposedException.ThrowIf(_disposed, GetType().Name);
        }

        /// <inheritdoc/>
        public virtual void Dispose()
        {
            if (_disposed) return;

            _lock.EnterWriteLock();
            try
            {
                // 释放所有处理器实例
                foreach (var handler in _handlers.Values)
                {
                    handler.Dispose();
                }
                _handlers.Clear();
            }
            finally
            {
                _lock.ExitWriteLock();
                _lock.Dispose();
                _disposed = true;
            }
            GC.SuppressFinalize(this);
        }
    }

    /// <summary>
    /// 文件处理器基类，提供基础文件信息访问功能
    /// </summary>
    /// <typeparam name="T">具体的处理器类型（CRTP 模式）</typeparam>
    public abstract class ResourceItem<T> : IDisposable where T : ResourceItem<T>
    {
        private readonly ResourceCollection<T> _manager;
        private FileInfo? _file;
        private bool _disposed;

        /// <summary>
        /// 获取文件的标识值
        /// </summary>
        public string Hash { get; }

        /// <summary>
        /// 获取文件扩展名（包含前导点）
        /// </summary>
        public string Extension { get; }

        /// <summary>
        /// 获取对应的文件信息对象（延迟加载）
        /// </summary>
        /// <exception cref="ObjectDisposedException">对象已释放时抛出</exception>
        public FileInfo File
        {
            get
            {
                ObjectDisposedException.ThrowIf(_disposed, GetType().Name);
                return _file ??= new FileInfo(_manager.GetFilePath(Hash, Extension));
            }
        }

        /// <summary>
        /// 初始化文件处理器实例
        /// </summary>
        /// <param name="manager">所属的文件管理器</param>
        /// <param name="hash">文件标识值</param>
        /// <param name="extension">文件扩展名</param>
        protected ResourceItem(ResourceCollection<T> manager, string hash, string extension)
        {
            _manager = manager;
            Hash = hash;
            Extension = extension;
        }

        /// <inheritdoc/>
        public virtual void Dispose()
        {
            _disposed = true;
            GC.SuppressFinalize(this);
        }
    }
}